package com.apexsoft.front.common.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class RSAUtil {

	private static final String ENCODE = "UTF-8";

	private static PublicKey getPublicKey(InputStream fis) {
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			X509Certificate cert2 = (X509Certificate) cf.generateCertificate(fis);
			return cert2.getPublicKey();
		} catch (CertificateException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private static PrivateKey getPrivateKey(InputStream fis, String password) {

		try {
			KeyStore ks = KeyStore.getInstance("PKCS12");
			ks.load(fis, password.toCharArray());
			fis.close();
			Enumeration enumas = ks.aliases();
			String keyAlias = null;
			if (enumas.hasMoreElements()) {
				keyAlias = (String) enumas.nextElement();
			}
			PrivateKey priKey = (PrivateKey) ks.getKey(keyAlias, password.toCharArray());
			return priKey;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private static String encodeByPublic(PublicKey publicKey, String sourcedata) {
		ByteArrayOutputStream out = null;
		try {
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(1, publicKey);
			out = new ByteArrayOutputStream();
			byte[] sourcebyte = sourcedata.getBytes(ENCODE);
			byte[] encryptData = null;
			for (int i = 0; i < sourcebyte.length; i += 245) {
				byte[] temp = Arrays.copyOfRange(sourcebyte, i, i + 245);
				encryptData = cipher.doFinal(temp);
				out.write(encryptData, 0, encryptData.length);
			}
			String encodeStr = new BASE64Encoder().encode(out.toByteArray());
			return encodeStr;
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} finally {
			try {
				if (out != null) {
					out.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	private static String decodeByPrivate(PrivateKey privateKey, String sourcedata) {
		ByteArrayOutputStream out = null;
		try {
			byte[] tempData = new BASE64Decoder().decodeBuffer(sourcedata);
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(2, privateKey);
			out = new ByteArrayOutputStream();
			byte[] encryptData = null;
			for (int i = 0; i < tempData.length; i += 256) {
				byte[] temp = Arrays.copyOfRange(tempData, i, i + 256);
				encryptData = cipher.doFinal(temp);
				out.write(encryptData, 0, encryptData.length);
			}
			return new String(out.toByteArray(), ENCODE);
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (out != null) {
					out.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * @Title: encode
	 * @Description: 加密
	 * @param publicKeyIs
	 * @param sourcedata
	 * @return
	 * @throws Exception
	 */
	public static String encode(InputStream publicKeyIs, String sourcedata) throws Exception {
		PublicKey publicKey = RSAUtil.getPublicKey(publicKeyIs);
		if (publicKey == null) {
			throw new Exception("公钥获取失败");
		}
		String encodeStr = RSAUtil.encodeByPublic(publicKey, sourcedata);
		if (encodeStr == null) {
			throw new Exception("加密失败");
		}
		return encodeStr;
	}

	/**
	 * @Title: decode
	 * @Description: 解密
	 * @param privateKeyIs
	 * @param password
	 * @param sourcedata
	 * @return
	 * @throws Exception
	 */
	public static String decode(InputStream privateKeyIs, String password, String sourcedata) throws Exception {
		PrivateKey privateKey = RSAUtil.getPrivateKey(privateKeyIs, password);
		if (privateKey == null) {
			throw new Exception("私钥获取失败");
		}
		String decodeStr = RSAUtil.decodeByPrivate(privateKey, sourcedata);
		if (decodeStr == null) {
			throw new Exception("解密失败");
		}
		return decodeStr.trim();
	}
}
